/*
 * Session.cpp
 *
 *  Created on: 2014. 6. 25.
 *      Author: jjaehuny
 */

#include "Session.h"

#include <boost/asio.hpp>

#include "../asio.h"

namespace Framework { namespace Network {

::Framework::Memory::Lock_Function_new_Pool<::Framework::Network::Session> s_clsSession_LockFunctionNewPool;

std::shared_ptr<::Framework::Network::Session>
create_session_multithread()
{
	return s_clsSession_LockFunctionNewPool.NEW();
}

static int s_index = 1;

} /*Network*/ } /*Framework*/



namespace Framework { namespace Network {

Session::Session() noexcept
: socket_(asio::Instance().io_service())
, resolver_(asio::Instance().io_service())
, observer_(NULL)
{
	// TODO Auto-generated constructor stub

	clsReadPacket_deque_.clear();
	clsSendPacket_deque_.clear();

	index_ = s_index++;
}

Session::~Session() {
	// TODO Auto-generated destructor stub
}

void
Session::Initialize()
{
}

void
Session::Finalize()
{
	LOG(INFO) << __PRETTY_FUNCTION__;

	boost::system::error_code ec;
	socket_.close(ec);
	if (ec)
	{
		LOG(INFO) << __PRETTY_FUNCTION__ << "close" << ec.value();
	}
	else
	{
		LOG(INFO) << __PRETTY_FUNCTION__ << "socket close success";
	}

	if (NULL != packet_)
		packet_ = NULL;

	clsReadPacket_deque_.clear();
	clsSendPacket_deque_.clear();

	if (NULL != observer_)
	{
		LOG(INFO) << __PRETTY_FUNCTION__ << "oserver is not null ";
		observer_ = NULL;
	}

}

std::shared_ptr<Session>
Session::CAPTURE()
{
	return shared_from_this();
}

void
Session::Connect(std::string & port)
{
	boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), "127.0.0.1", port.c_str());
	auto endpoint_iterator = resolver_.resolve(query);

	auto self(shared_from_this());
	auto function = [this](boost::system::error_code ec, boost::asio::ip::tcp::resolver::iterator)
	{
		if (!ec)
		{
			LOG(INFO) << __PRETTY_FUNCTION__;
			//do_read();
			do_read_header();

			if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::OPEN);
		}
		else
		{
			if (NULL != observer_) observer_->OnConnectFail(ec);
		}
	};

	boost::asio::async_connect(socket_, endpoint_iterator, function);
}

void
Session::OnStart(boost::asio::ip::tcp::socket socket)
{
	socket_ = std::move(socket);

	/// SO_REUSEADDR 옵션을 사용하면, TIME_WAIT 상태에 있는 PORT를 사용할 수 있다.
	socket_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
	/// socket 옵션을 이용해서 TIME_WAIT이 발생하지 않도록 하는 코드
	socket_.set_option(boost::asio::ip::tcp::acceptor::linger(true, 0));

	socket_.set_option(boost::asio::ip::tcp::acceptor::keep_alive(true));

	LOG(INFO) << __PRETTY_FUNCTION__;

	if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::OPEN);

	//do_read();
	do_read_header();
}

void
Session::Close()
{
	asio::Instance().io_service().post([this]() {
		boost::system::error_code ec;
		socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
		if (ec)
		{
			LOG(INFO) << __PRETTY_FUNCTION__ << "shutdown " << "[error : " << ec.value() << "]";
		}
		else
		{
			LOG(INFO) << __PRETTY_FUNCTION__ << "socket shutdown success";
		}
	});
}

void
Session::do_read()
{
	packet_ = clsReadPacketPool_.NEW();

	auto self(shared_from_this());
	auto function = [this, self](boost::system::error_code ec, std::size_t length)
	{
		if (!ec)
		{
		  clsReadPacket_deque_.push_back(packet_);
		  if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::MESSAGE);

		  do_read();
		}
		else
		{
			if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::CLOSE);
		}
	};

	socket_.async_read_some(boost::asio::buffer(packet_->Buffer(), packet_->capacity()), function);
}

void
Session::do_read_header() {
	packet_ = clsReadPacketPool_.NEW();
	packet_->set_head_length_(observer_->get_head_length());

	auto self(shared_from_this());
	auto function = [this, self](boost::system::error_code ec, std::size_t length)
	{
		if (!ec)
		{
			short size = static_cast<short>(* packet_->Buffer());
			short command = static_cast<short>( * (packet_->Buffer() + sizeof(short)));
			packet_->set_body_length_(size);
			packet_->set_command(command);

			LOG(INFO) << __PRETTY_FUNCTION__ <<"[size : " << size << "] [command : " << command << "]";
			do_read_body();
		}
		else
		{
			if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::CLOSE);
		}
	};

	boost::asio::async_read(socket_, boost::asio::buffer(packet_->Buffer(), packet_->get_head_length_()), function);
}

void
Session::do_read_body() {
	auto self(shared_from_this());
	auto function = [this, self](boost::system::error_code ec, std::size_t length)
	{
		if (!ec)
		{
			LOG(INFO) << __PRETTY_FUNCTION__ <<"[size : " << packet_->get_body_length_() << "] [command : " << packet_->get_command() << "]";

			clsReadPacket_deque_.push_back(packet_);
			if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::MESSAGE);

			do_read_header();
		}
		else
		{
			if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::CLOSE);
		}
	};

	boost::asio::async_read(socket_, boost::asio::buffer(packet_->BodyBuffer(), packet_->get_body_length_()), function);
}

void
Session::do_write()
{
	auto self(shared_from_this());
	auto function = [this, self](boost::system::error_code ec, std::size_t length)
	{
		if (!ec)
		{
			bool isEmpty = false;
			{
				std::lock_guard<std::mutex> lock(send_mutex_);
				clsSendPacket_deque_.pop_front();

				if (true == clsSendPacket_deque_.empty())
					isEmpty = true;
			}

			if (false == isEmpty)
				do_write();
		}
		else
		{
			if (NULL != observer_) observer_->OnEvent(Observer::E_EVENT::CLOSE);
		}
	};

	std::shared_ptr<Packet> packet;
	{
		std::lock_guard<std::mutex> lock(send_mutex_);
		packet = clsSendPacket_deque_.front();
	}

	boost::asio::async_write(socket_, boost::asio::buffer(packet->Buffer(), packet->get_send_length()), function);
}

} /*Network*/ } /*Framework*/

